package cn.thoughtworks.school.services;


import cn.thoughtworks.school.entities.SimpleCodingQuiz;
import cn.thoughtworks.school.entities.Tag;
import cn.thoughtworks.school.entities.User;
import cn.thoughtworks.school.entities.dto.AssignmentQuizData;
import cn.thoughtworks.school.handlers.BusinessException;
import cn.thoughtworks.school.repositories.SimpleCodingQuizRepository;
import cn.thoughtworks.school.requestParams.CreateSimpleCodingParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.persistence.criteria.Predicate;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class SimpleCodingQuizService {
  @Autowired
  private SimpleCodingQuizRepository simpleCodingQuizRepository;
  @Autowired
  private Utils utils;
  @Value("${simple-coding-quiz-jenkins-url.add}")
  private String addSimpleCodingQuizJenkinsUrl;
  @Value("${github.token}")
  private String githubToken;
  @Value("${quiz-center-url}")
  private String quizCenterUrl;
  @Value("${github.cert}")
  private String githubCert;
  @Autowired
  private QuizService quizService;
  @Autowired
  private UserCenterService userCenterService;

  public SimpleCodingQuiz addSimpleCodingQuiz(CreateSimpleCodingParam params, User user) {
    params.setBranch(getBranch(params));
    Long organizationId = userCenterService.getUserCurrentOrganizationId(user.getId());
    SimpleCodingQuiz simpleCodingQuiz = SimpleCodingQuiz.builder()
      .makerId(user.getId())
      .organizationId(organizationId)
      .tags(utils.formatTags(params.getTags(), user.getId()))
      .definitionRepo(params.getDefinitionRepo())
      .homeworkName(params.getTitle())
      .description(params.getDescription())
      .referenceAnswer(params.getReferenceAnswer())
      .stackId(params.getStackId())
      .createTime(new Date())
      .branch(params.getBranch())
      .isAvailable(true).build();
    simpleCodingQuiz = simpleCodingQuizRepository.save(simpleCodingQuiz);

    judgeQuiz(simpleCodingQuiz);
    return simpleCodingQuiz;
  }

  private String getBranch(CreateSimpleCodingParam params) {
    return "".equals(params.getBranch()) ? "master" : params.getBranch();
  }

  private String getCodingName(SimpleCodingQuiz SimpleCodingQuiz) {
    String defineRepo = SimpleCodingQuiz.getDefinitionRepo();
    return defineRepo.substring(defineRepo.lastIndexOf("/") + 1, defineRepo.lastIndexOf("."));
  }

  public void deleteById(Long id) throws BusinessException {
    SimpleCodingQuiz simpleCodingQuiz = simpleCodingQuizRepository.findById(id).orElseThrow(() -> new BusinessException("Currente quiz is not exist."));

    simpleCodingQuiz.setAvailable(false);
    simpleCodingQuizRepository.save(simpleCodingQuiz);
  }

  public void editSimpleCodingQuiz(CreateSimpleCodingParam params, User user) throws BusinessException {
    params.setBranch(getBranch(params));
    SimpleCodingQuiz simpleCodingQuiz = simpleCodingQuizRepository.findById(params.getId())
      .orElseThrow(() -> new BusinessException("Current quiz is not exist"));
    simpleCodingQuiz.edit(params, utils.formatTags(params.getTags(), user.getId()));
    simpleCodingQuizRepository.save(simpleCodingQuiz);
    judgeQuiz(simpleCodingQuiz);
  }

  private void judgeQuiz(SimpleCodingQuiz simpleCodingQuiz) {
    RestTemplate template = new RestTemplate();

    String callbackUrl = quizCenterUrl + "/api/simpleCodingQuizzes/" + simpleCodingQuiz.getId();

    MultiValueMap data = new LinkedMultiValueMap();
    data.add("github_token", githubToken);
    data.add("git_url", simpleCodingQuiz.getDefinitionRepo());
    data.add("callback_url", callbackUrl);
    data.add("github_cert", githubCert);
    data.add("coding_name", getCodingName(simpleCodingQuiz));
    data.add("branch", simpleCodingQuiz.getBranch());
    template.postForObject(addSimpleCodingQuizJenkinsUrl, data, String.class);
  }

  public Page getSimpleCodingQuizzesPageable(Pageable pageable, Long id, Long userId, String description, List<String> tags, User user) {
    Long currentOrganizationId = userCenterService.getUserCurrentOrganizationId(user.getId());
    List<Long> quizIds = getQuizIdsByTags(tags);
    Page<SimpleCodingQuiz> homeworkQuizPage = getSimpleCodingQuizPage(currentOrganizationId, pageable, id, userId, description, quizIds);

    return utils.format(homeworkQuizPage, pageable);
  }
  private Page<SimpleCodingQuiz> getSimpleCodingQuizPage(Long organizationId, Pageable pageable, Long id, Long userId, String description, List<Long> quizIds) {
    return simpleCodingQuizRepository.findAll((Specification<SimpleCodingQuiz>) (root, query, criteriaBuilder) -> {
      List<Predicate> predicates = quizService.getDefaultPredicates(organizationId, id, userId, description, quizIds, root, criteriaBuilder);
      return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
    }, pageable);
  }

  private List<Long> getQuizIdsByTags(List<String> tags) {
    List<SimpleCodingQuiz> quizzes = simpleCodingQuizRepository.findQuizzesByTags(tags);
    if (Objects.nonNull(tags) && quizzes.isEmpty()) {
      return Arrays.asList(-1L);
    }
    return quizzes.stream().map(SimpleCodingQuiz::getId).collect(Collectors.toList());
  }

  public List<AssignmentQuizData> copy(List<AssignmentQuizData> simpleCodingQuizzesData) {
    List<Long> ids = simpleCodingQuizzesData.stream().map(AssignmentQuizData::getQuizId).collect(Collectors.toList());
    List<SimpleCodingQuiz> simpleCodingQuizzes = simpleCodingQuizRepository.findByIdIn(ids);

    simpleCodingQuizzesData.forEach(quizData -> {
      Long creatorId = quizData.getCreatorId();
      SimpleCodingQuiz simpleCodingQuiz = simpleCodingQuizzes.stream()
        .filter(quiz -> quiz.getId().equals(quizData.getQuizId()))
        .findFirst().get();

      Set<Tag> tags = quizService.copyTags(quizData.getOrganizationId(), simpleCodingQuiz.getTags(), creatorId);
      Long stackId = quizService.copyStack(quizData.getOrganizationId(), simpleCodingQuiz.getStackId(), creatorId);
      SimpleCodingQuiz quiz = SimpleCodingQuiz.copy(simpleCodingQuiz, creatorId, quizData.getOrganizationId(),tags,stackId);
      simpleCodingQuizRepository.save(quiz);
      quizData.setNewQuizId(quiz.getId());
    });
    return simpleCodingQuizzesData;
  }
}
